using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Reflection;

using SypConst.Garbage;
using SypEngine;
using SypConst.Path;



namespace SypEngine
{
    public enum SyncAction : int
    {
        ADD_ZIP = 8,
        ADD_INDEX = 7,
        COPY_PICTURE = 6,
        SUPPR_CONF = 5,
        COPY_FILE = 4,
        ADD_FOLDER = 3,
        SUPPR_FOLDER = 2,
        ADD_FILE = 1,
        SUPPR_FILE = 0
    }

    public enum SyncMode : int
    {
        ONLINE = 1,
        LOCAL = 0
    }

    [Serializable]
    public class SypActionObj
    {
        public SypActionObj() { }
        public Object SypObj = null;
        public SyncAction Action;
        private uint retry = 5;

        public bool CanRetry()
        {
            bool result = true;
            if (this.retry == 0)
                result = false;
            this.retry--;
            return result;
        }

    }

    public class SypSyncTable
    {
        public int SiteId = -1;
        public Queue<SypActionObj> LocalQueue = null;
        public Queue<SypActionObj> OnlineQueue = null;
    }


    public class SypSiteHandler : SypObject
    {
        private readonly Object _locker = new Object();

        public List<SypSite> Sites = null;
        public List<int> SiteIdUsed = null;
        private Hashtable SiteSyncList = null;

        #region thread safe methods

        public List<int> getSiteIds()
        {
            List<int> listSiteIds = new List<int>();
            lock (SiteSyncList.SyncRoot)
            {
                if (SiteSyncList.Keys != null)
                {
                    foreach (int key in this.SiteSyncList.Keys)
                        listSiteIds.Add(key);
                }
            }
            return listSiteIds;
        }

        public List<SypSyncTable> getSiteSyncs()
        {
            List<SypSyncTable> listSites = new List<SypSyncTable>();
            lock (SiteSyncList.SyncRoot)
            {
                if (SiteSyncList.Values != null)
                {
                    foreach (SypSyncTable ssync in this.SiteSyncList.Values)
                        listSites.Add((SypSyncTable)ssync);
                }
            }
            return listSites;
        }

        public void LoadOnlineQueue(int SiteId, List<SypActionObj> actionsToLoad)
        {
            lock (SiteSyncList.SyncRoot)
            {
                if (!SiteSyncList.ContainsKey(SiteId))
                {
                    SypSyncTable SypSiteTable = new SypSyncTable();
                    SypSiteTable.LocalQueue = new Queue<SypActionObj>();
                    SypSiteTable.OnlineQueue = new Queue<SypActionObj>(actionsToLoad);
                    SypSiteTable.SiteId = SiteId;
                    SiteSyncList.Add(SiteId, SypSiteTable);
                }
                else
                {
                    SypSyncTable SypSiteTable = (SypSyncTable)SiteSyncList[SiteId];
                    SypSiteTable.OnlineQueue = null;
                    SypSiteTable.OnlineQueue = new Queue<SypActionObj>(actionsToLoad);
                }
            }
        }

        public void LoadLocalQueue(int SiteId, List<SypActionObj> actionsToLoad)
        {
            lock (SiteSyncList.SyncRoot)
            {
                if (!SiteSyncList.ContainsKey(SiteId))
                {
                    SypSyncTable SypSiteTable = new SypSyncTable();
                    SypSiteTable.LocalQueue = new Queue<SypActionObj>(actionsToLoad);
                    SypSiteTable.OnlineQueue = new Queue<SypActionObj>();
                    SypSiteTable.SiteId = SiteId;
                    SiteSyncList.Add(SiteId, SypSiteTable);
                }
                else
                {
                    SypSyncTable SypSiteTable = (SypSyncTable)SiteSyncList[SiteId];
                    SypSiteTable.LocalQueue = null;
                    SypSiteTable.LocalQueue = new Queue<SypActionObj>(actionsToLoad);
                }
            }
        }

        public void RevertToOnlineQueue(int SiteId, List<SypActionObj> actionsToLoad)
        {
            lock (SiteSyncList.SyncRoot)
            {
                if (SiteSyncList.ContainsKey(SiteId))
                {
                    SypSyncTable SypSiteTable = (SypSyncTable)SiteSyncList[SiteId];

                    if (SypSiteTable.OnlineQueue != null)
                    {
                        Queue CopyOnlineQueue = new Queue();
                        while (SypSiteTable.OnlineQueue.Count > 0)
                        {
                            SypActionObj Action = (SypActionObj)SypSiteTable.OnlineQueue.Dequeue();
                            CopyOnlineQueue.Enqueue(Action);
                        }

                        SypSiteTable.OnlineQueue = null;
                        SypSiteTable.OnlineQueue = new Queue<SypActionObj>(actionsToLoad);

                        while (CopyOnlineQueue.Count > 0)
                        {
                            SypActionObj Action = (SypActionObj)CopyOnlineQueue.Dequeue();
                            SypSiteTable.OnlineQueue.Enqueue(Action);
                        }
                        SypGarbage.free(CopyOnlineQueue);
                    }
                }
            }
        }

        public Queue<SypActionObj> CopyOnlineQueue(int SiteId){
            lock (SiteSyncList.SyncRoot)
            {
                Queue<SypActionObj> CopyOnlineQueue = new Queue<SypActionObj>();
                if (SiteSyncList.ContainsKey(SiteId))
                {
                    SypSyncTable QueueTable = (SypSyncTable)SiteSyncList[SiteId];
                    if (QueueTable.OnlineQueue != null)
                    {
                        while (QueueTable.OnlineQueue.Count > 0)
                        {
                            SypActionObj Action = (SypActionObj)QueueTable.OnlineQueue.Dequeue();
                            CopyOnlineQueue.Enqueue(Action);
                        }
                    }
                }
                return CopyOnlineQueue;
            }
        }

        public void RemoveSyncTable(int SiteId)
        {
            lock (SiteSyncList.SyncRoot)
            {
                if (SiteSyncList.ContainsKey(SiteId) && !ExistSiteId(SiteId))
                {
                    SiteSyncList.Remove(SiteId);
                }
            }
        }

        public void RemoveSyncTables()
        {
            lock (SiteSyncList.SyncRoot)
            {
                SypGarbage.free(SiteSyncList);
                SiteSyncList = new Hashtable();
            }
        }

        public void ClearQueues()
        {
            lock (SiteSyncList.SyncRoot)
            {
                foreach (int SiteId in SiteSyncList.Keys)
                {
                    SypSyncTable QueueTable = (SypSyncTable)SiteSyncList[SiteId];
                    if (QueueTable.LocalQueue != null)
                        QueueTable.LocalQueue.Clear();
                    if (QueueTable.OnlineQueue != null)
                        QueueTable.OnlineQueue.Clear();
                }

            }
        }

        public void ClearQueues(int SiteId)
        {
            lock (SiteSyncList.SyncRoot)
            {
                if (SiteSyncList.ContainsKey(SiteId))
                {
                    SypSyncTable QueueTable = (SypSyncTable)SiteSyncList[SiteId];
                    if (QueueTable.LocalQueue != null)
                        QueueTable.LocalQueue.Clear();
                    if (QueueTable.OnlineQueue != null)
                        QueueTable.OnlineQueue.Clear();
                }
            }
        }

        public void AddAction(int SiteId, Object myObj, SyncAction action, SyncMode mode)
        {
            lock (SiteSyncList.SyncRoot)
            {
                if (!SiteSyncList.ContainsKey(SiteId))
                {
                    SypSyncTable SypSiteTable = new SypSyncTable();
                    SypSiteTable.LocalQueue = new Queue<SypActionObj>();
                    SypSiteTable.OnlineQueue = new Queue<SypActionObj>();
                    SypSiteTable.SiteId = SiteId;
                    SiteSyncList.Add(SiteId, SypSiteTable);
                }

                SypSyncTable mySiteTable = (SypSyncTable)SiteSyncList[SiteId];
                if (mySiteTable != null)
                {
                    if (mode == SyncMode.LOCAL)
                    {
                        SypActionObj LocalActObj = new SypActionObj();
                        LocalActObj.SypObj = myObj;
                        LocalActObj.Action = action;
                        mySiteTable.LocalQueue.Enqueue(LocalActObj);
                    }
                    else if (mode == SyncMode.ONLINE)
                    {
                        SypActionObj OnlineActObj = new SypActionObj();
                        OnlineActObj.SypObj = myObj;
                        OnlineActObj.Action = action;
                        mySiteTable.OnlineQueue.Enqueue(OnlineActObj);
                    }
                }
            }
        }

        public bool SyncExists(int SiteId)
        {
            lock (SiteSyncList.SyncRoot)
            {
                return (SiteSyncList.ContainsKey(SiteId)) ? true : false;
            }
        }

        public SypSyncTable GetSync(int SiteID)
        {
            lock (SiteSyncList.SyncRoot)
            {
                return (SiteSyncList.ContainsKey(SiteID)) ? (SypSyncTable)SiteSyncList[SiteID] : null;
            }
        }

        #endregion

        public List<SypVisitor> GetVisitors()
        {
            return this.VisitorList;
        }
        
        public SypSiteHandler(SetText OutputMethod, VisitorCallBack[] Callbacks)
            : base(OutputMethod)
        {
            if (SiteIdUsed == null)
                SiteIdUsed = new List<int>();

            if (Sites == null)
                Sites = new List<SypSite>();
            if (VisitorList != null)
                VisitorList = new List<SypVisitor>();

            SiteSyncList = new Hashtable();

            Assembly currentAssembly = Assembly.LoadFrom(SypGarbage.ASSEMBLY_NAME);

            foreach (Type cls in currentAssembly.GetTypes())
            {
                if (cls.IsPublic && cls.BaseType!= null && cls.BaseType.Equals(typeof(SypVisitor)))
                {
                   ConstructorInfo ctor = cls.GetConstructor(new Type[]{typeof(SetText), typeof(VisitorCallBack[])});
                    if (ctor != null)
                    {
                        object instance = instance = ctor.Invoke(new object[] { OutputMethod, Callbacks});

                        if (instance is SypVisitor)
                            VisitorList.Add((SypVisitor)instance);
                    }

                }
            }

        }


        public void SerializeToXml(string destinationfile, SypSite SiteConf)
        {
            try
            {
                XmlSerializer MySerializer = new XmlSerializer(typeof(SypSite), new Type[] { typeof(List<Category>), typeof(List<Album>), typeof(List<int>), typeof(Category), typeof(Album), typeof(TransferConf) });
                StreamWriter MyWriter = new StreamWriter(destinationfile, false, System.Text.Encoding.UTF8);
                MySerializer.Serialize(MyWriter, SiteConf);
                MyWriter.Close();
            }
            catch (Exception)
            {
                OutputMethod("Unexpected error while processing serialization of : " + destinationfile, Output.ERROR);
            }
        }

        public void SerializeToXml(SypSite SiteConf)
        {
            //string destinationfile = SypGarbage.SITES_CONF + SiteConf.SiteId + SypGarbage.CONF_EXTENSION;
            string destinationfile = SypPathHandler.GetPath(SypPathMode.LOCAL, SypPathRef.SITE_CONF, null, SiteConf.SiteId); 
            try
            {
                XmlSerializer MySerializer = new XmlSerializer(typeof(SypSite), new Type[] { typeof(List<Category>), typeof(List<Album>), typeof(List<int>), typeof(Category), typeof(Album), typeof(TransferConf) });
                StreamWriter MyWriter = new StreamWriter(destinationfile, false, System.Text.Encoding.UTF8);
                MySerializer.Serialize(MyWriter, SiteConf);
                MyWriter.Close();
            }
            catch (Exception)
            {
                OutputMethod("Unexpected error while processing serialization of : " + destinationfile, Output.ERROR);
            }
        }

        public SypSite LoadFromXml(string sourcefile)
        {
            try
            {
                XmlSerializer MySerializer = new XmlSerializer(typeof(SypSite), new Type[] { typeof(List<Category>), typeof(List<Album>), typeof(List<int>), typeof(Category), typeof(Album), typeof(TransferConf) });
                FileStream MyStream = new FileStream(sourcefile, FileMode.Open);
                SypSite SiteConfig = (SypSite)MySerializer.Deserialize(MyStream);
                MyStream.Close();
                return SiteConfig;
            }
            catch (Exception)
            {
                OutputMethod("Unexpected error while processing unserialization of : " + sourcefile, Output.ERROR);
                return null;
            }
        }

        public void SerializeSites()
        {
            foreach (SypSite Sconf in Sites)
            {
                this.SerializeToXml(SypPathHandler.GetPath(SypPathMode.LOCAL, SypPathRef.SITE_CONF, null, Sconf.SiteId), Sconf);
            }
        }


        public void SerializeSite(int siteId)
        {
            SypSite mySite = GetSiteConfig(siteId);
            if (mySite != null)
            {
                try
                {
                    SerializeToXml(mySite);
                    if (mySite.AlbumIdToReload != -1)
                    {
                        string page_online = SypPathHandler.GetPath(SypPathMode.ONLINE, SypPathRef.ALBUM_PAGE, null, mySite.SiteId, -1, mySite.AlbumIdToReload);
                        string redirection_online = "<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;URL=" + page_online + "\">";

                        StreamWriter Wstream = new StreamWriter(SypPathHandler.GetPath(SypPathMode.LOCAL, SypPathRef.BO_PAGE, null, mySite.SiteId), false);
                        Wstream.Write(redirection_online);
                        Wstream.Close();
                        AddAction(siteId, SypPathHandler.GetPath(SypPathMode.ONLINE, SypPathRef.BO_PAGE, null, mySite.SiteId), SyncAction.ADD_INDEX, SyncMode.ONLINE);
                        mySite.AlbumIdToReload = -1;                    
                    }
                    else if (mySite.CategorysList != null && mySite.CategorysList.Count > 0)
                    {
                        Category cat = mySite.CategorysList[0];
                        if (cat.AlbumList != null && cat.AlbumList.Count > 0)
                        {
                            Album alb = cat.AlbumList[0];
                            string page_online = SypPathHandler.GetPath(SypPathMode.ONLINE, SypPathRef.ALBUM_PAGE, null, mySite.SiteId, cat.Id, alb.id);
                            string redirection_online = "<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;URL=" + page_online + "\">";

                            StreamWriter Wstream = new StreamWriter(SypPathHandler.GetPath(SypPathMode.LOCAL, SypPathRef.BO_PAGE, null, mySite.SiteId), false);
                            Wstream.Write(redirection_online);
                            Wstream.Close();
                            AddAction(siteId, SypPathHandler.GetPath(SypPathMode.ONLINE, SypPathRef.BO_PAGE, null, mySite.SiteId), SyncAction.ADD_INDEX, SyncMode.ONLINE);
                        }
                    }
                }
                catch (Exception)
                {
                    OutputMethod("ca pete dans le SerializeSite", Output.ERROR);
                }
            }
        }




        public void CheckAvailableSites()
        {
            string[] SiteConfigFiles = null;
            if (Directory.Exists(SypPathHandler.GetPath(SypPathMode.LOCAL, SypPathRef.SITE_CONF_FOLDER, null)))
                SiteConfigFiles = System.IO.Directory.GetFiles(SypPathHandler.GetPath(SypPathMode.LOCAL, SypPathRef.SITE_CONF_FOLDER, null), SypPathHandler.GetPath(SypPathMode.LOCAL, SypPathRef.SITE_CONF_FILTER, null));
            else
                SiteConfigFiles = null;

            if (SiteConfigFiles != null)
            {
                foreach (string SiteConf in SiteConfigFiles)
                {
                    SypSite Sconf = this.LoadFromXml(SiteConf);
                    if (Sconf != null)
                    {
                        SiteIdUsed.Add(Sconf.SiteId);
                        Sites.Add(Sconf);
                    }

                }
            }
        }


        public SypSite GetSiteConfig(string SiteName)
        {
                foreach (SypSite site in Sites)
                {
                    if (site.SiteTitle.Equals(SiteName))
                        return site;
                }
                return null;
        }

        public bool ExistSiteId(int SiteId)
        {
            foreach (SypSite site in Sites)
            {
                if (site.SiteId == SiteId)
                    return true;
            }
            return false;
        }


        public SypSite GetSiteConfig(int SiteId)
        {
            foreach (SypSite site in Sites)
            {
                if (site.SiteId == SiteId)
                    return site;
            }
            return null;
        }


        public void AddSite(SypSite Sconf)
        {
            if (Sites == null)
                Sites = new List<SypSite>();
            Sites.Add(Sconf);
            SiteIdUsed.Add(Sconf.SiteId);
        }
    }

}
